#include <QtCore/QCryptographicHash>
#include <QtGui/QApplication>
#include <QyDebug.h>
#include <QpwDecorationObject.h>
#include <QpwGeometry.h>
#include <QyTexturePool.h>
#include "QpwContentManager.h"
#include "QpwContentManager_p.h"
#include "QpwGeometryEdit.h"
#include "ui_QpwStyleEditDialog.h"
#include "ui_QpwAccountEditDialog.h"

/**************************************************************************************************/

class QpwStyleEdit : public QDialog, public Ui::QpwStyleEditDialog {
public:
    QpwStyleEdit() {

        setupUi(this);
        updateBaseValues();

    }

    QpwStyle style() const { return p_style; }

    void setStyle( const QpwStyle & style ) {

        p_style = style;

        setWindowTitle( QString("Style %1").arg( style.uuid.toString() ) );
        nameEdit->setText(style.name);
        colorEdit->setText( style.color.name() );
        textureEdit->setCurrentIndex( textureEdit->findData(style.texture) );
        shaderEdit->setCurrentIndex( textureEdit->findData(style.shader) );

    }

    void updateBaseValues() {

        textureEdit->clear();
        shaderEdit->clear();

        textureEdit->addItem( "none", QString() );
        shaderEdit->addItem( "none", QString() );

        foreach ( QString textureName, QyTexturePool::staticPool()->textures.keys() )
            textureEdit->addItem( textureName, textureName );

    }

private:
    void accept() {

        p_style.name = nameEdit->text();
        p_style.color = QColor( colorEdit->text() );
        p_style.texture = ( textureEdit->currentIndex() > 0 ) ? textureEdit->currentText() : QString();
        p_style.shader = ( shaderEdit->currentIndex() > 0 ) ? shaderEdit->currentText() : QString();

        QDialog::accept();

    }

    QpwStyle p_style;

};

/**************************************************************************************************/

class QpwAccountEdit : public QDialog, public Ui::QpwAccountEditDialog {
public:
    QpwAccountEdit() {

        setupUi(this);

        accessLevelEdit->addItem( QpwAccount::accessLevelName(QpwAccount::GuestAccessLevel), QpwAccount::GuestAccessLevel );
        accessLevelEdit->addItem( QpwAccount::accessLevelName(QpwAccount::UserAccessLevel), QpwAccount::UserAccessLevel );
        accessLevelEdit->addItem( QpwAccount::accessLevelName(QpwAccount::AdminAccessLevel), QpwAccount::AdminAccessLevel );
        accessLevelEdit->addItem( QpwAccount::accessLevelName(QpwAccount::DeveloperAccessLevel), QpwAccount::DeveloperAccessLevel );

    }

    QpwAccount account() const { return p_account; }

    void setAccount( const QpwAccount & account ) {

        p_account = account;

        setWindowTitle( QString("Account %1").arg( p_account.uuid.toString() ) );
        userNameEdit->setText(p_account.username);
//         passwordEdit->setText(p_account.password);
        accessLevelEdit->setCurrentIndex( accessLevelEdit->findData(p_account.accessLevel) );

    }

private:
    bool checkInput() {

        if ( userNameEdit->text().isEmpty() ) {

            statusLabel->setText( tr("<b>Error</b>: User name field is empty.") );
            return false;
        }

        if ( passwordEdit->text().isEmpty() ) {

            statusLabel->setText( tr("<b>Error</b>: Password field is empty.") );
            return false;
        }

        if ( confirmPasswordEdit->text().isEmpty() ) {

            statusLabel->setText( tr("<b>Error</b>: Confirm password field is empty.") );
            return false;
        }

        if ( passwordEdit->text() != confirmPasswordEdit->text() ) {

            statusLabel->setText( tr("<b>Error</b>: Password and Confirm password fields is not equal.") );
            return false;
        }


        QRegExp regExpUserName("[a-zA-Z]([a-zA-Z0-9]*)"),
            regExpPassword("([a-zA-Z0-9]*)");

        if ( !regExpUserName.exactMatch( userNameEdit->text() ) ) {

            statusLabel->setText( tr("<b>Error</b>: Invalid user name string.") );

            return false;
        }

        if ( !regExpPassword.exactMatch( passwordEdit->text() ) ) {

            statusLabel->setText( tr("<b>Error</b>: Invalid password string.") );

            return false;
        }

        return true;
    }
    void accept() {

        if ( !checkInput() ) {

            QDialog::rejected();
            return;
        }

        p_account.username = userNameEdit->text();
//         p_account.password = passwordEdit->text();
        p_account.password = QCryptographicHash::hash( passwordEdit->text().toAscii(), QCryptographicHash::Md5 ).toHex();
        p_account.accessLevel = QpwAccount::AccessLevels( accessLevelEdit->itemData(
            accessLevelEdit->currentIndex() ).toUInt() );

        QDialog::accept();

    }

    QpwAccount p_account;

};

/**************************************************************************************************/

template < typename DataType >
class ContentItem : public QTreeWidgetItem {
public:
    explicit ContentItem( QTreeWidget * treeWidget, DataType & data )
        : QTreeWidgetItem(treeWidget), dataLink(data) {}

    DataType & dataLink;

};

/**************************************************************************************************/

QpwSubTypeEdit::QpwSubTypeEdit() {

    setupUi(this);
    baseTypeEdit->addItem(
        QpwWorld::DecorationObject::typeName(QpwWorld::DecorationObject::DecorationObjectType),
        QpwWorld::DecorationObject::DecorationObjectType
    );
    baseTypeEdit->addItem(
        QpwWorld::DecorationObject::typeName(QpwWorld::DecorationObject::StaticObjectType),
        QpwWorld::DecorationObject::StaticObjectType
    );
    baseTypeEdit->addItem(
        QpwWorld::DecorationObject::typeName(QpwWorld::DecorationObject::QuasiStaticObjectType),
        QpwWorld::DecorationObject::QuasiStaticObjectType
    );
    baseTypeEdit->addItem(
        QpwWorld::DecorationObject::typeName(QpwWorld::DecorationObject::DynamicObjectType),
        QpwWorld::DecorationObject::DynamicObjectType
    );
    updateBaseValues();

}

void QpwSubTypeEdit::setSubType( const QpwSubType & subType ) {

    p_subType = subType;

    setWindowTitle( QString("Sub type %1").arg( subType.uuid.toString() ) );
    baseTypeEdit->setCurrentIndex( baseTypeEdit->findData(subType.baseType) );
    styleEdit->setCurrentIndex( styleEdit->findData( subType.styleUuid.toString() ) );
    nameEdit->setText(subType.name);
    descriptionEdit->setText(subType.description);
    healthEdit->setValue(subType.health);
    geometryEdit->setCurrentIndex( geometryEdit->findData( subType.geometryUuid.toString() ) );
    eventProcessorEdit->setCurrentIndex(
        eventProcessorEdit->findData(subType.eventProcessorType) );
    destroyProcessorEdit->setCurrentIndex(
        destroyProcessorEdit->findData(subType.destroyProcessorType) );

}

void QpwSubTypeEdit::updateBaseValues() {

    styleEdit->clear();
    eventProcessorEdit->clear();
    destroyProcessorEdit->clear();

    styleEdit->addItem( "none", QUuid().toString() );
    geometryEdit->addItem( "none", QUuid().toString() );

    eventProcessorEdit->addItem( "none", 0 );
    destroyProcessorEdit->addItem( "none", 0 );

}

void QpwSubTypeEdit::accept() {

    p_subType.baseType = ( baseTypeEdit->currentIndex() > 0 )
        ? QpwWorld::DecorationObject::ObjectTypes( baseTypeEdit->itemData(
            baseTypeEdit->currentIndex() ).toUInt() )
        : QpwWorld::DecorationObject::DecorationObjectType;
    p_subType.styleUuid = ( styleEdit->currentIndex() > 0 )
        ? styleEdit->itemData( styleEdit->currentIndex() ).toString() : QString();
    p_subType.name = nameEdit->text();
    p_subType.description = descriptionEdit->toPlainText();
    p_subType.health = healthEdit->value();
    p_subType.geometryUuid = ( geometryEdit->currentIndex() > 0 )
        ? geometryEdit->itemData( geometryEdit->currentIndex() ).toString() : QString();
    p_subType.eventProcessorType = ( eventProcessorEdit->currentIndex() > 0 )
        ? eventProcessorEdit->itemData( eventProcessorEdit->currentIndex() ).toUInt() : 0;
    p_subType.destroyProcessorType = ( eventProcessorEdit->currentIndex() > 0 )
        ? eventProcessorEdit->itemData( eventProcessorEdit->currentIndex() ).toUInt() : 0;

    QDialog::accept();

}

/**************************************************************************************************/

QpwContentManager::QpwContentManager( QWidget * parent ) : QWidget(parent) {

    setupUi(this);
    geometryEditor = new QpwGeometryEdit(geometryEdit);
    currentTab = Tabs( tabWidget->currentIndex() );
    setDataDirPath( qApp->applicationDirPath() + "/../data" );
    updateContent(currentTab);

}

QpwContentManager::~QpwContentManager() {
}

void QpwContentManager::setDataDirPath( const QString & dataDirPath ) {

    p_dataDirPath = dataDirPath;
    QyTexturePool::staticPool()->basePath = dataDirPath;
    QyDbgValue(dataDirPath);
    qDebug("!!!!!!!!!!");
    QyTexturePool::staticPool()->loadFrom("");
    qDebug("  !!!!!!!!!!");
    updateContent();

}

void QpwContentManager::updateContent() {

    updateContent(StyleEditTab);
    updateContent(GeometryTab);
    updateContent(SybTypeEditTab);
    updateContent(AccountManagerTab);

}

void QpwContentManager::clearContent() {

    styleView->clear();
    subTypeView->clear();
    accountView->clear();
    geometryView->clear();

}

void QpwContentManager::on_addButton_clicked() {

    switch (currentTab) {

    case StyleEditTab:
        {

            QpwStyle style = editStyle();

//             if ( style.isValid() )
                add( styleDatabase.add(style) );

        }
        break;

    case SybTypeEditTab:
        {

            QpwSubType subType = editSubType();

//             if ( subType.isValid() )
                add( subTypeDatabase.add(subType) );

        }
        break;

    case AccountManagerTab:
        {

            QpwAccount account = editAccount();

//             if ( account.isValid() )
                add( accountManager.add(account) );

        }
        break;

    case GeometryTab:
        {

            QpwWorld::Geometry geometry;

            geometry.name = tr("Geometry #%1").arg( geometryDatabase.rows.count() );

            add( geometryDatabase.add(geometry) );

        }
        break;

    }

}

void QpwContentManager::on_removeButton_clicked() {

    switch (currentTab) {

    case StyleEditTab:

        if ( ContentItem<QpwStyle> * item
            = dynamic_cast<ContentItem<QpwStyle>*>( styleView->currentItem() ) )
        {

            styleDatabase.rows.remove(item->dataLink.uuid);
            delete item;

        }

        break;

    case SybTypeEditTab:

        if ( ContentItem<QpwSubType> * item
            = dynamic_cast<ContentItem<QpwSubType>*>( subTypeView->currentItem() ) )
        {

            subTypeDatabase.rows.remove(item->dataLink.uuid);
            delete item;

        }

        break;

    case AccountManagerTab:

        if ( ContentItem<QpwAccount> * item
            = dynamic_cast<ContentItem<QpwAccount>*>( accountView->currentItem() ) )
        {

            accountManager.rows.remove(item->dataLink.uuid);
            delete item;

        }
        break;

    case GeometryTab:

        if ( ContentItem<QpwWorld::Geometry> * item
            = dynamic_cast<ContentItem<QpwWorld::Geometry>*>( geometryView->currentItem() ) )
        {

            geometryDatabase.rows.remove(item->dataLink.uuid);
            delete item;

        }
        break;

    }

}

void QpwContentManager::on_clearButton_clicked() {

    switch (currentTab) {

    case StyleEditTab:
        styleView->clear();
        styleDatabase.rows.clear();
        break;

    case SybTypeEditTab:
        subTypeView->clear();
        subTypeDatabase.rows.clear();
        break;

    case AccountManagerTab:
        accountView->clear();
        accountManager.rows.clear();
        break;

    case GeometryTab:
        geometryView->clear();
        geometryEditor->clear();
        geometryDatabase.rows.clear();
        break;

    }

}

void QpwContentManager::on_saveButton_clicked() {

    styleDatabase.save( p_dataDirPath + "/styles.xml" );
    subTypeDatabase.save( p_dataDirPath + "/subtypes.xml" );
    accountManager.save( p_dataDirPath + "/accounts.xml" );
    geometryDatabase.save( p_dataDirPath + "/geometrys.xml" );

}

void QpwContentManager::on_cancelButton_clicked() {

    updateContent();

}

void QpwContentManager::on_tabWidget_currentChanged( int currentIndex ) {

    currentTab = Tabs(currentIndex);
    updateContent(currentTab);

}

void QpwContentManager::on_styleView_itemActivated( QTreeWidgetItem * item, int column ) {

    if ( ContentItem<QpwStyle> * contentItem = dynamic_cast<ContentItem<QpwStyle>*>(item) ) {

        contentItem->dataLink = editStyle(contentItem->dataLink);
        update( contentItem, contentItem->dataLink );

    }

}

void QpwContentManager::on_subTypeView_itemActivated( QTreeWidgetItem * item, int column ) {

    if ( ContentItem<QpwSubType> * contentItem = dynamic_cast<ContentItem<QpwSubType>*>(item) ) {

        contentItem->dataLink = editSubType(contentItem->dataLink);
        update( contentItem, contentItem->dataLink );

    }

}

void QpwContentManager::on_accountView_itemActivated( QTreeWidgetItem * item, int column ) {

    if ( ContentItem<QpwAccount> * contentItem = dynamic_cast<ContentItem<QpwAccount>*>(item) ) {

        contentItem->dataLink = editAccount(contentItem->dataLink);
        update( contentItem, contentItem->dataLink );

    }

}

void QpwContentManager::on_geometryView_itemActivated( QTreeWidgetItem * item, int column ) {

    if ( ContentItem<QpwWorld::Geometry> * contentItem = dynamic_cast<ContentItem<QpwWorld::Geometry>*>(item) ) {

        editGeometry(contentItem->dataLink);

    } else {

        geometryEditor->clear();

    }

}

void QpwContentManager::on_geoEditApplyButton_clicked() {

    if ( ContentItem<QpwWorld::Geometry> * contentItem
        = dynamic_cast<ContentItem<QpwWorld::Geometry>*>( geometryView->currentItem() ) )
    {

        contentItem->dataLink = geometryEditor->geometry();
        contentItem->dataLink.name = geoEditNameEdit->text();

        update( contentItem, contentItem->dataLink );

    }

}

void QpwContentManager::on_geoEditRemoveAllButton_clicked() {

    geometryEditor->clear();

}

void QpwContentManager::add( const QpwStyle & style ) {

    ContentItem<QpwStyle> * item
        = new ContentItem<QpwStyle>( styleView, styleDatabase.rows[style.uuid] );
    update( item, style );

}

void QpwContentManager::add( const QpwSubType & subType ) {

    ContentItem<QpwSubType> * item
        = new ContentItem<QpwSubType>( subTypeView, subTypeDatabase.rows[subType.uuid] );
    update( item, subType );

}

void QpwContentManager::add( const QpwAccount & account ) {

    ContentItem<QpwAccount> * item
        = new ContentItem<QpwAccount>( accountView, accountManager.rows[account.uuid] );
    update( item, account );

}

void QpwContentManager::add( const QpwWorld::Geometry & geometry ) {

    ContentItem<QpwWorld::Geometry> * item
        = new ContentItem<QpwWorld::Geometry>( geometryView, geometryDatabase.rows[geometry.uuid] );
    update( item, geometry );

}

void QpwContentManager::updateContent( Tabs tab ) {

    switch (tab) {

    case StyleEditTab:

        styleDatabase.load( p_dataDirPath + "/styles.xml" );
        if ( styleDatabase.rows.count() ) {

            styleView->clear();
            QMapIterator<QUuid,QpwStyle> iter(styleDatabase.rows);

            while ( iter.hasNext() ) {

                iter.next();
                add( iter.value() );

            }

        }

        break;

    case SybTypeEditTab:

        subTypeDatabase.load( p_dataDirPath + "/subtypes.xml" );
        if ( subTypeDatabase.rows.count() ) {

            subTypeView->clear();
            QMapIterator<QUuid,QpwSubType> iter(subTypeDatabase.rows);

            while ( iter.hasNext() ) {

                iter.next();
                add( iter.value() );

            }

        }

        break;

    case AccountManagerTab:

        accountManager.load( p_dataDirPath + "/accounts.xml" );
        if ( accountManager.rows.count() ) {

            accountView->clear();
            QMapIterator<QUuid,QpwAccount> iter(accountManager.rows);

            while ( iter.hasNext() ) {

                iter.next();
                add( iter.value() );

            }

        }

        break;

    case GeometryTab:

        geometryDatabase.load( p_dataDirPath + "/geometrys.xml" );
        if ( geometryDatabase.rows.count() ) {

            geometryView->clear();
            geometryEditor->clear();
            QMapIterator<QUuid,QpwWorld::Geometry> iter(geometryDatabase.rows);

            while ( iter.hasNext() ) {

                iter.next();
                add( iter.value() );

            }

        }

        break;

    }

}

void QpwContentManager::update( QTreeWidgetItem * item, const QpwStyle & style ) {

    if ( ContentItem<QpwStyle> * contentItem = dynamic_cast<ContentItem<QpwStyle>*>(item) ) {

        contentItem->setText( 0, style.uuid.toString() );
        contentItem->setText( 1, style.name );
        contentItem->setText( 2, style.color.name() );
        contentItem->setText( 3, style.texture.isEmpty() ? "none" : style.texture );
        contentItem->setText( 4, style.shader.isEmpty() ? "none" : style.shader );

    }

}

void QpwContentManager::update( QTreeWidgetItem * item, const QpwSubType & subType ) {

    if ( ContentItem<QpwSubType> * contentItem = dynamic_cast<ContentItem<QpwSubType>*>(item) ) {

        contentItem->setText( 0, subType.uuid.toString() );
        contentItem->setText( 1, QpwWorld::DecorationObject::typeName(subType.baseType) );
        contentItem->setText( 2, styleName(subType.styleUuid) );
        contentItem->setText( 3, subType.name );
        contentItem->setText( 4, subType.description );
        contentItem->setText( 5, QString::number(subType.health) );
        contentItem->setText( 6, geometryName(subType.geometryUuid) );
        contentItem->setText( 7, eventProcessorName(subType.eventProcessorType) );
        contentItem->setText( 8, destroyProcessorName(subType.destroyProcessorType) );

    }

}

void QpwContentManager::update( QTreeWidgetItem * item, const QpwAccount & account ) {

    if ( ContentItem<QpwAccount> * contentItem = dynamic_cast<ContentItem<QpwAccount>*>(item) ) {

        contentItem->setText( 0, account.uuid.toString() );
        contentItem->setText( 1, account.username );
        contentItem->setText( 2, account.password );
        contentItem->setText( 3, QpwAccount::accessLevelName(account.accessLevel) );

    }

}

void QpwContentManager::update( QTreeWidgetItem * item, const QpwWorld::Geometry & geometry ) {

    if ( ContentItem<QpwWorld::Geometry> * contentItem
        = dynamic_cast<ContentItem<QpwWorld::Geometry>*>(item) )
    {

        contentItem->setText( 0, geometry.uuid.toString() );
        contentItem->setText( 1, geometry.name );
        contentItem->setText( 2, tr( geometry.isCircle() ? "Circle" : "Polygon" ) );
        contentItem->setText( 3, QString::number( geometry.subGeometry.count() ) );

    }

}

QString QpwContentManager::styleName( const QUuid & uuid ) const {

    return styleDatabase.rows.contains(uuid) ? styleDatabase.rows[uuid].name : QString("<unknow>");
}

QString QpwContentManager::geometryName( const QUuid & uuid ) const {

    return geometryDatabase.rows.contains(uuid) ? geometryDatabase.rows[uuid].name : QString("<unknow>");
}

QString QpwContentManager::eventProcessorName( quint32 id ) const {

    Q_UNUSED(id);

    return QString("<unknow>");
}

QString QpwContentManager::destroyProcessorName( quint32 id ) const {

    Q_UNUSED(id);

    return QString("<unknow>");
}

QpwStyle QpwContentManager::editStyle( const QpwStyle & style ) const {

    QpwStyleEdit styleEdit;
    styleEdit.setStyle(style);

    return ( styleEdit.exec() == QDialog::Accepted ) ? styleEdit.style() : style;
}

QpwSubType QpwContentManager::editSubType( const QpwSubType & subType ) const {

    QpwSubTypeEdit subTypeEdit;
    QMapIterator<QUuid,QpwStyle> iter(styleDatabase.rows);

    while ( iter.hasNext() ) {

        iter.next();
        subTypeEdit.styleEdit->addItem( iter.value().name, iter.value().uuid.toString() );

    }

    QMapIterator<QUuid,QpwWorld::Geometry> geoIter(geometryDatabase.rows);

    while ( geoIter.hasNext() ) {

        geoIter.next();
        subTypeEdit.geometryEdit->addItem( geoIter.value().name, geoIter.value().uuid.toString() );

    }

    subTypeEdit.setSubType(subType);

    return ( subTypeEdit.exec() == QDialog::Accepted ) ? subTypeEdit.subType() : subType;
}

QpwAccount QpwContentManager::editAccount( const QpwAccount & account ) const {

    QpwAccountEdit accountEdit;
    accountEdit.setAccount(account);

    return ( accountEdit.exec() == QDialog::Accepted ) ? accountEdit.account() : account;
}

QpwWorld::Geometry QpwContentManager::editGeometry( const QpwWorld::Geometry & geometry ) const {

    geometryEditor->setGeometry(geometry);
    geoEditNameEdit->setText(geometry.name);

    return geometry;
}

/**************************************************************************************************/

int main( int argCount, char ** args ) {

    QApplication app( argCount, args );
    QpwContentManager contentManager;

    contentManager.show();

    return app.exec();
}
